#include <rtthread.h>
#include <stdio.h>
#include <stdint.h>
#include "DAP.h"
#include "DAP_Config.h"
#include "dap_if_config.h"
#include "mfifoblock.h"
#include "usbd_core.h"
#include "usbd_dap_bulk.h"

static uint8_t REQUEST_IDLE_FLAG = 0;
static uint8_t RESPONSE_IDLE_FLAG = 0;
static uint8_t DAP_HANDLE_FALG = 0;

static struct mfifoblock MFB_REQUEST = {0};
static struct mfifoblock MFB_RESPONSE = {0};

rt_align(RT_ALIGN_SIZE) static uint8_t __REQUEST_BUFFER[DAP_PACKET_COUNT][DAP_PACKET_SIZE] = {0};
rt_align(RT_ALIGN_SIZE) static uint8_t __RESPONSE_BUFFER[DAP_PACKET_COUNT][DAP_PACKET_SIZE] = {0};
static mblock_t REQUEST_BLOCK[DAP_PACKET_COUNT] = {0};
static mblock_t RESPONSE_BLOCK[DAP_PACKET_COUNT] = {0};

// DAP event
// #define DAP_EVENT_REQUEST  (1 << 1)
// #define DAP_EVENT_RESPONSE (1 << 2)
#define DAP_EVENT_HANDLE (1 << 3)
#define DAP_EVENT_ALL    (0xFF)

static struct rt_event usbd_dap_bulk_event;

static struct rt_thread thread_usbd_dap_bulk;
rt_align(RT_ALIGN_SIZE) static uint8_t thread_stack[512];

static int usbd_dap_bulk_buffer_init(void)
{
    rt_kprintf("REQUEST_BUFFER: %p\n", &MFB_REQUEST);
    rt_kprintf("RESPONSE_BUFFER: %p\n", &MFB_RESPONSE);

    mfifoblock_map(REQUEST_BLOCK, (uint8_t *)__REQUEST_BUFFER, DAP_PACKET_COUNT, DAP_PACKET_SIZE);
    mfifoblock_init(&MFB_REQUEST, REQUEST_BLOCK, DAP_PACKET_COUNT, DAP_PACKET_SIZE);

    mfifoblock_map(RESPONSE_BLOCK, (uint8_t *)__RESPONSE_BUFFER, DAP_PACKET_COUNT, DAP_PACKET_SIZE);
    mfifoblock_init(&MFB_RESPONSE, RESPONSE_BLOCK, DAP_PACKET_COUNT, DAP_PACKET_SIZE);

    return 0;
}
INIT_PREV_EXPORT(usbd_dap_bulk_buffer_init);

void usbd_dap_bulk_process(void *p)
{
    while (1)
    {
        if (REQUEST_IDLE_FLAG)
        {
            static uint8_t *buf = NULL;
            if (mfifoblock_write_block_get(&MFB_REQUEST, &buf) == 0)
            {
                // static uint32_t request_count = 0;
                // rt_kprintf(" MFB_REQUEST[%d] get buf: %p\n", request_count++, buf);
                usbd_ep_start_read(DAP_OUT_EP, buf, DAP_PACKET_SIZE);
                REQUEST_IDLE_FLAG = 0;
            }
        }

        if (RESPONSE_IDLE_FLAG)
        {
            static uint8_t *buf = NULL;
            static rt_uint16_t bufsize = 0;
            if (mfifoblock_read_block_get(&MFB_RESPONSE, &buf, &bufsize) == 0)
            {
                // static uint32_t response_count = 0;
                // rt_kprintf("MFB_RESPONSE[%d] get buf: %p %d\n", response_count++, buf, bufsize);
                usbd_ep_start_write(DAP_IN_EP, buf, bufsize);
                RESPONSE_IDLE_FLAG = 0;
            }
        }

        {
            static uint8_t *request = NULL;
            static uint8_t *response = NULL;
            static rt_uint16_t response_len = 0;
            // rt_kprintf("DAP_ExecuteCommand ready try\n");
            if ((mfifoblock_read_block_get(&MFB_REQUEST, &request, NULL) == 0) &&
                (mfifoblock_write_block_get(&MFB_RESPONSE, &response) == 0))
            {
                DAP_HANDLE_FALG = 1;
                response_len = DAP_ExecuteCommand(request, response);
                mfifoblock_read_block_increase(&MFB_REQUEST);
                mfifoblock_write_block_increase(&MFB_RESPONSE, response_len);
                continue;
            }
            else
            {
                DAP_HANDLE_FALG = 0;
            }
        }

        if (rt_event_recv(&usbd_dap_bulk_event, DAP_EVENT_ALL, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, 100, RT_NULL) !=
            RT_EOK)
        {
            // rt_kprintf("usbd_dap_bulk_event ret: %d\n", ret);
            // rt_kprintf("MFB_REQUEST wi: %ld, ri: %ld\n", (uint32_t)MFB_REQUEST.write_count,
            //            (uint32_t)MFB_REQUEST.read_count);
            // rt_kprintf("MFB_RESPONSE wi: %ld, ri: %ld\n", (uint32_t)MFB_RESPONSE.write_count,
            //            (uint32_t)MFB_RESPONSE.read_count);

            if (RESPONSE_IDLE_FLAG == 0)
            {
                mfifoblock_reset(&MFB_REQUEST);
                mfifoblock_reset(&MFB_RESPONSE);
                REQUEST_IDLE_FLAG = 1;
                RESPONSE_IDLE_FLAG = 1;
                DAP_HANDLE_FALG = 0;
                rt_kprintf("DAP mfifoblock reset\n");
            }
        }
    }
}

void usbd_dap_bulk_event_configured(void)
{
    rt_thread_control(&thread_usbd_dap_bulk, RT_THREAD_CTRL_CLOSE, RT_NULL);

    // rt_kprintf("%s\n", __FUNCTION__);
    REQUEST_IDLE_FLAG = 0;
    RESPONSE_IDLE_FLAG = 0;
    DAP_HANDLE_FALG = 0;

    mfifoblock_reset(&MFB_REQUEST);
    mfifoblock_reset(&MFB_RESPONSE);

    DAP_Setup();

    rt_event_init(&usbd_dap_bulk_event, "usbd_dap_bulk_event", RT_IPC_FLAG_PRIO);

    rt_thread_init(&thread_usbd_dap_bulk, "usbd_dap_bulk_process", usbd_dap_bulk_process, RT_NULL, thread_stack,
                   sizeof(thread_stack), 1, 10);

    rt_thread_control(&thread_usbd_dap_bulk, RT_THREAD_CTRL_STARTUP, RT_NULL);
}

void usbd_dap_bulk_event_disconnected(void)
{
    // DAP_Setup();
    // rt_kprintf("%s\n", __FUNCTION__);
    // rt_thread_control(&thread_usbd_dap_bulk, RT_THREAD_CTRL_CLOSE, RT_NULL);
}

void usbd_dap_bulk_out_callback(uint8_t ep, rt_uint32_t nbytes)
{
    // rt_kprintf("%s\n", __FUNCTION__);
    static uint8_t *buf = NULL;

    mfifoblock_write_block_increase(&MFB_REQUEST, nbytes);

    if (mfifoblock_write_block_get(&MFB_REQUEST, &buf) == 0)
    {
        usbd_ep_start_read(DAP_OUT_EP, buf, DAP_PACKET_SIZE);
    }
    else
    {
        REQUEST_IDLE_FLAG = 1;
    }

    if (DAP_HANDLE_FALG == 0)
    {
        rt_event_send(&usbd_dap_bulk_event, DAP_EVENT_HANDLE);
    }
}

void usbd_dap_bulk_in_callback(uint8_t ep, rt_uint32_t nbytes)
{
    // rt_kprintf("%s\n", __FUNCTION__);
    static uint8_t *buf = NULL;
    static rt_uint16_t bufsize = 0;

    mfifoblock_read_block_increase(&MFB_RESPONSE);

    if (mfifoblock_read_block_get(&MFB_RESPONSE, &buf, &bufsize) == 0)
    {
        // rt_kprintf("%s usbd_ep_start_write\n", __FUNCTION__);
        usbd_ep_start_write(DAP_IN_EP, buf, bufsize);
    }
    else
    {
        RESPONSE_IDLE_FLAG = 1;
    }

    if (DAP_HANDLE_FALG == 0)
    {
        rt_event_send(&usbd_dap_bulk_event, DAP_EVENT_HANDLE);
    }
}
